package uk.ac.standrews.cs.cs2001.practical02.s110008568.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.Map.Entry;

import uk.ac.standrews.cs.cs2001.practical02.common.NoCopiesException;
import uk.ac.standrews.cs.cs2001.practical02.common.NotBorrowedException;
import uk.ac.standrews.cs.cs2001.practical02.interfaces.IBook;
import uk.ac.standrews.cs.cs2001.practical02.interfaces.ILibrary;

public class Library implements ILibrary {
    private HashMap<Book, Integer> bookCopies;
    private HashMap<Book, Integer> bookCopiesAvailable;
    // we assume that the library is linear in time, so the next
    // book that can be borrowed cannot be borrowed before its predecessor
    private Book lastlyBorrowed;
    private HashMap<Book, Integer> bookBorrowingNumber;

    public Library() {
        bookCopies = new HashMap<Book, Integer>();
        bookCopiesAvailable = new HashMap<Book, Integer>();
        bookBorrowingNumber = new HashMap<Book, Integer>();
        lastlyBorrowed = null;
    }

    @Override
    public void addCopy(IBook book) {
        if (book != null) {
            bookCopies.put((Book) book, getNumberOfCopies(book) + 1);
            // update the number of available copies of the book
            bookCopiesAvailable.put((Book) book, getNumberAvailable(book) + 1);
        }
    }

    @Override
    public void borrowCopy(IBook book, Date date) throws NoCopiesException {
        int available = getNumberAvailable(book);
        if (available > 0) {
            bookCopiesAvailable.put((Book) book, available - 1);
            lastlyBorrowed = (Book) book;
            bookBorrowingNumber.put((Book) book,
                    getNumberOfBorrowings(book) + 1);
        } else {
            throw new NoCopiesException();
        }
    }

    @Override
    public void returnCopy(IBook book, Date date) throws NotBorrowedException {
        int available = getNumberAvailable(book);
        int borrowed = getNumberOfCopies(book) - available;
        if (borrowed > 0) {
            bookCopiesAvailable.put((Book) book, available + 1);
        } else {
            throw new NotBorrowedException();
        }

    }

    @Override
    public int getNumberofBooks() {
        return bookCopies.size();
    }

    @Override
    public int getNumberOfCopies() {
        int copiesNumber = 0;
        for (Entry<Book, Integer> entry : bookCopies.entrySet()) {
            copiesNumber += entry.getValue();
        }
        return copiesNumber;
    }

    @Override
    public int getNumberOfCopies(IBook book) {
        return bookCopies.containsKey(book) ? bookCopies.get(book) : 0;
    }

    @Override
    public int getNumberAvailable(IBook book) {
        return bookCopiesAvailable.containsKey(book) ? bookCopiesAvailable
                .get(book) : 0;
    }

    @Override
    public int getNumberOfBorrowings(IBook book) {
        return bookBorrowingNumber.containsKey(book) ? bookBorrowingNumber
                .get(book) : 0;
    }

    @Override
    public IBook getMostPopular() {
        int mostPopular = Integer.MIN_VALUE;
        Book mostPopularBook = null;
        for (Entry<Book, Integer> entry : bookBorrowingNumber.entrySet()) {
            if (entry.getValue() > mostPopular) {
                mostPopular = entry.getValue();
                mostPopularBook = entry.getKey();
            }
        }

        return mostPopularBook;
    }

    @Override
    public IBook getMostRecentlyBorrowed() {
        return lastlyBorrowed;
    }

}
